package cap.jeeves.socialnetstar.backend

/*
 * User records for jeeves social net case study.
 * @author jeanyang, kuat
 */

import cap.scalasmt._
import collection.immutable.ListSet
import SocialNetStarBackend._
import cap.jeeves.JeevesTypes._
import cap.scalasmt.Expr._
import com.weiglewilczek.slf4s.Logging

case class Name(s: String) extends JeevesRecord
case class Email(s: String) extends JeevesRecord
case class SocialCircle(s: String) extends JeevesRecord

case class Entry(e: String, l: PrivacyLevel) extends JeevesRecord
//{
// 
//  def applyMkSensitive()= mkSensitive(l,this)
// 
//}


class SocialNetStarUserRecord(
	/*
	 * <attribute>V: actual value
	 * <attribute>L: privacy level */
	nameV: Name,
	nameL: PrivacyLevel,
	emailV: Email,
	emailL: PrivacyLevel,
	circleV: SocialCircle,
	circleL: PrivacyLevel,
	friendsL: PrivacyLevel,
	wallL: PrivacyLevel
	)
	extends JeevesRecord with Logging {
	private var friendsV: Set[SocialNetStarUserRecord] = Set()
	var long: IntExpr = 1000
	var lat: IntExpr = 1000
	private var wallV: Set[Entry] = Set()

	/** Mutators */

	def addFriend(u: SocialNetStarUserRecord) {
	  friendsV = friendsV + u
	  logger.info("AddFriend: "+this.name.toString()+" befriends "+u.name.toString())
	  }

	def removeFriend(u: SocialNetStarUserRecord) {
	  friendsV = friendsV - u
	  logger.info("RemoveFriend: "+this.name.toString()+" unfriends "+u.name.toString())
	  }

	def setLocation(long_x: BigInt, lat_y: BigInt) {
		val l = mkLevel();
		//ctxt: current context
		restrict(l, (ctxt: Sensitive) => distance(ctxt, this) < 10);
		long = mkSensitiveInt(l, long_x, 1000);
		lat = mkSensitiveInt(l, lat_y, 1000);
	}

	def addEntry(e: Entry) {
	  wallV = wallV + e
	  logger.info("AddEntry: "+this.name.toString()+" adds an entry to his/her wall which reads: "+e.e+" with PrivacyLevel: "+e.l)
	  }

	def removeEntry(e: Entry) {
	  wallV = wallV - e
	  logger.info("RemoveEntry: "+this.name.toString()+" removes an entry from his/her wall which reads: "+e.e+" with PrivacyLevel: "+e.l)

	}

	def shareEntryWith(e:Entry, u:SocialNetStarUserRecord)(implicit ctxt:Sensitive=this) = {
		// add e to u's entries set
		val l=u.getWallLevel()
		//mkSensitive(l,u.addEntry(e))
		u.addEntry(e)
		// TODO: use Jeeves policies when doing this
	    logger.info("ShareEntryWith: "+this.name.toString()+" shares an entry with: "+u.name.toString()+" wich reads: "+e.e+" with PrivacyLevel: "+e.l)

	}	

	def getWallLevel()=level(wallL)

	/** Observers */
	val name = mkSensitive(level(nameL), nameV)
	val email = mkSensitive(level(emailL), emailV)
	val circle = mkSensitive(level(circleL), circleV)

	def getFriends() = {
		val l = level(friendsL);
		friendsV.map(mkSensitive(l, _))
	}

	def getWall()={
		wallV.map(entry => mkSensitive(level(entry.l),entry))
	}

	def isFriends(u: SocialNetStarUserRecord) = getFriends.has(u)

	def location() = (long, lat);

	/** Helpers */
	private def level(ul: PrivacyLevel): LevelVar = {
			val l = mkLevel();
			//ctxt: current context
			def me(implicit ctxt: Sensitive) = ctxt === this;
			ul match {
			case OnlySelf => restrict(l, (ctxt: Sensitive) => me(ctxt))
			case OnlyFriends =>
			restrict(l,
					(ctxt: Sensitive) => (me(ctxt) || friendsV.has(ctxt)));
			case Anyone =>
			}
			l
	}

	private def abs(expr: IntExpr): IntExpr = {
			jif(expr >= 0, _ => expr, _ => -expr)
	}

	private def distance(a: Sensitive, b: Sensitive) =
			abs(a.long - b.long) + abs(a.lat - b.lat)

}
